package com.devops.admin.util;

import cn.hutool.core.util.StrUtil;
import com.devops.constant.DevOpsConstant;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author hyb22
 * @version 1.0.0
 * @description yaml文件增删改查工具类
 * @date 2022/10/25 13:35
 */
@Slf4j
public class YamlUtils {

    private final static DumperOptions OPTIONS = new DumperOptions();
    //重新生成yaml文件，原yaml文件中的注释会消失。
    static {
        //设置yaml读取方式为块读取
        OPTIONS.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        OPTIONS.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
        OPTIONS.setPrettyFlow(false);
    }

    /**
     * 获取修改后的yaml文件
     * @param key
     * @param map
     * @param yaml
     * @return
     */
    public static String getUpdateYaml(String key,Map<String,Object> map,String yaml){
        String newYaml = setYaml(key, map, yaml);
        return getResultYaml(key, yaml, newYaml,null);
    }

    /**
     * @Author heyabo
     * @Description 为yaml文件添加标签
     * @Date 15:39 2023/1/12
     * @Param [k8sMetadata, map, value]
     * @return java.lang.String
     **/
    public static String addMetadataLabels(String k8sMetadata, Map<String, Object> map, String value) {
       String newYaml = addLabels(k8sMetadata,map,value);
       String key = DevOpsConstant.K8sConfig.K8S_METADATA + "." + DevOpsConstant.K8sConfig.K8S_LABELS;
       return getResultYaml(k8sMetadata,value,newYaml,key);
    }

    private static String getResultYaml(String key, String yaml, String newYaml, String newKey) {
        String resultYaml = yaml;
        AtomicReference<String> labelsValue = new AtomicReference<>();
        String[] split = key.split(",");
        String replaceYaml = "";
        Object oldVal = getValue(split[split.length-1], getYamlToMap(yaml));
        ((Map<String,Object >)oldVal).forEach((k,v)->{
            if (v instanceof Map){
                Map<String,Object> map = (Map<String,Object>)v;
                map.forEach((keys,value) -> labelsValue.set(keys + ": " + value));
            } else {
                labelsValue.set(k + ": " + v);
            }
        });
        if (StrUtil.isNotEmpty(newKey)) {
            AtomicReference<String> value = new AtomicReference<>();
            Object val = getValue(newKey, getYamlToMap(newYaml));
            ((Map<String,Object >)val).forEach((k,v)-> value.set(k+": "+v));
            replaceYaml = newYaml.substring(0, newYaml.indexOf(value.get()) + value.get().length());
        } else {
            replaceYaml = newYaml.substring(0, newYaml.lastIndexOf(labelsValue.get()) + labelsValue.get().length());
        }
        String isReplacedYaml = resultYaml.substring(0, resultYaml.lastIndexOf(labelsValue.get())+ labelsValue.get().length());
        resultYaml = resultYaml.replace(isReplacedYaml,replaceYaml);
        return resultYaml;
    }

    public static String addLabels(String k8sMetadata, Map<String, Object> map, String value) {
        Map<String, Object> yamlToMap = getYamlToMap(value);
        Object objValue = yamlToMap.get(k8sMetadata);
        if (objValue == null){
            log.error("不存在key...");
        }
        if (objValue instanceof Map){
            Map<String, Object> resultMap = (Map<String, Object>)objValue;
            resultMap.put(DevOpsConstant.K8sConfig.K8S_LABELS,map.get(DevOpsConstant.K8sConfig.K8S_LABELS));
            yamlToMap.put(k8sMetadata,resultMap);
        }
        Yaml yaml = new Yaml(OPTIONS);
        return yaml.dump(yamlToMap);
    }

    /**
     * 修改yaml
     * @param key
     * @param map
     * @param yaml
     * @return
     */
    private static String setYaml(String key,Map<String,Object> map,String yaml){
        String[] keys = key.split(",");
        for (int i = 0; i < keys.length; i++) {
            yaml = updateYaml(keys[i],map,yaml);
            if (i < keys.length - 2){
                yaml = setYaml(keys[i+1],map,yaml);
            }
        }

        return yaml;
    }

    /**
     * 根据key获取yaml文件的内容
     * @param key
     * @param yaml
     * @return
     */
    public static Object getValues(String key,String yaml){

        return getValue(key,getYamlToMap(yaml));
    }

    /**
     * 解析yaml文件
     * @param yaml
     * @return
     */
    public static Map<String,Object> analysisYaml(String yaml){

        return getYamlToMap(yaml);
    }

    /**
     * 将Yaml配置文件转换成map
     * @param fileName
     * @return
     */
    private static Map<String, Object> getYamlToMap(String fileName) {
        LinkedHashMap<String, Object> yamls = new LinkedHashMap<>();
        Yaml yaml = new Yaml();
        try {
            InputStream inputStream = new ByteArrayInputStream(fileName.getBytes(StandardCharsets.UTF_8));
            yamls = yaml.loadAs(inputStream, LinkedHashMap.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return yamls;
    }

    /**
     * 根据key获取yaml文件中属性值
     * @param key
     * @param yamlMap
     * @return
     */
    private static Object getValue(String key, Map<String, Object> yamlMap) {
        String[] keys = key.split("[.]");
        Object obj = yamlMap.get(keys[0]);
        if (key.contains(".")) {
            if (obj instanceof Map) {
                return getValue(key.substring(key.indexOf('.') + 1), (Map<String, Object>) obj);
            } else if (obj instanceof List) {
                return getValue(key.substring(key.indexOf('.') + 1), (Map<String, Object>) ((List<Object>)obj).get(0));
            } else if (obj instanceof String){
                return obj;
            }else {
                return null;
            }
        }
        return obj;
    }

    /**
     * 使用递归的方式更改map中的值
     * @param map
     * @param key 指定哪个键
     * @param value 即将更改的值
     * @return
     */
    private static Map<String, Object> setValue(Map<String, Object> map, String key, Object value) throws Exception {
        String[] keys = key.split("\\.");
        int len = keys.length;
        Map temp = map;
        for (int i = 0; i < len - 1; i++) {
            if (temp.containsKey(keys[i])) {
                Object obj = temp.get(keys[i]);
                if (obj instanceof Map) {
                    temp = (Map) obj;
                } else if (obj instanceof List) {
                    temp = (Map) ((List<?>) obj).get(0);
                } else {
                    throw new Exception("temp类型错误");
                }
            } else {
                return null;
            }
            if (i == len - 2) {
                temp.put(keys[i + 1], value);
            }
        }
        for (int j = 0; j < len - 1; j++) {
            if (j == len - 1) {
                map.put(keys[j], temp);
            }
        }
        return map;
    }

    /**
     * 修改yaml中属性的值
     * @param key key是properties的方式： aaa.bbb.ccc (key不存在不修改)
     * @param value 新的属性值 （新属性值和旧属性值一样，不修改）
     * @param yamlName
     * @return true 修改成功，false 修改失败
     */
    private static String updateYaml(String key, Object value, String yamlName) {
        Object data = null;
        Map<String, Object> yamlToMap = getYamlToMap(yamlName);
        if (null == yamlToMap) {
            return yamlName;
        }

        Object oldVal = getValue(key, yamlToMap);

        // 未找到key，不修改
        if (null == oldVal) {
            log.error("key is not found!");
            return yamlName;
        }

        // 不是最小节点值，不修改
//        if (oldVal instanceof Map) {
//            System.out.println("input key is not last node!");
//            return false;
//        }

//        if (value == oldVal) {
//            System.out.println("newVal equals oldVal!");
//            return false;
//        }
        if (oldVal instanceof Map && value instanceof Map) {
            Map<String, Object> oldMap = (Map<String, Object>) oldVal;
            Map<String, Object> newMap = (Map<String, Object>) value;
            oldMap.forEach((k, v) -> {
                if (!newMap.containsKey(k)) {
                    newMap.put(k, v);
                }
            });

            data = newMap;
        }
        Yaml yaml = new Yaml(OPTIONS);
        try {
            Map<String, Object> resultMap = setValue(yamlToMap, key, data);
            if (resultMap != null) {
                return yaml.dump(setValue(yamlToMap, key, value));
            } else {
                return yamlName;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return yamlName;
    }


}
